package io.spring.sample.graphql.geography;


import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import io.spring.sample.graphql.filter.Node;
import io.spring.sample.graphql.repository.util.Tool;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;


//聚合表; 最大5级行政级别。 比较权威的 区划代码库 http://www.axunxun.com/daima/
//最小的行政单位;  数据库搜索查询基础对象。
//监察才有自贸区，针对单位的属性，并不是直接针对设备的属性。这丫自贸区没必要限定那个最小地理区域？--行政层级-=区县级的机关,实际管辖-1;
//自贸区？，一个地址既是普通行政区属，又是自贸区属: 自贸区可以包括多个最小的行政单元单元；福州片区、厦门片区和平潭片区还要区分；附带关联属性表的？if(IN[,])else;
//没法：自贸区比起最小的行政单元还要更加的细分的地域概念，和楼盘很像，但是会跨越多行政区划的。自贸区只能额外增加标识关联属性？特别对待区域标志符号。
//自贸区可以特殊例外处理，前端处理，人工辨识。

/**地址和行政的管理单元； 级别行政的代表；树状5级行政关系网；
 * 一般最基层才需要配置一个Adminunit，其他高级别也可以添加的。
 * 这个给地理区划用的，？不一定适合行政机关哦。
 * ？行政管理的层级中的某个标注；并非一定最小的Town镇级别。地市级别的行政机关标注：county以下为空的。
 * 特别的管理单元{目的是给Unit做行政许可区域配套管理}：name:福建省福州市{};
 * 支持Adminunit数据不是非要最小的行政区划！举例可以是"美国加州旧金山"“英国伦敦”就可以了。
 *【？】 福州13县（市）区市场监管局--高新区局：｛单独一个市场监管分局｝【没连在一起的多个孤立地理区】=?不是自贸区?两种概念{两个版本的};
 * 高新区托管闽侯县南屿镇和上街镇5个村(上街镇的建平、厚庭、新洲、马排、马保等五个村),{底下管辖了：南屿镇},实际上还是有地理概念。
 * 福州高新技术开发区的行政区划目前仍然属于闽侯县；代管；在治安、卫生、司法、行政级别等还是属于闽侯。
 * 高新区应该独立配置一个Adminunit才更容易操作。自贸区和高新区并不一样，但两者可能有重叠区域。高新区是有地理区分的。
 * 允许自贸区也能单独设置Adminunit{不一定必须都是有地理区域的管理单元，Adminunit可以虚的没有明确可点击出来的地理地图},自贸区和单位管理直接挂钩。
 * 自贸区没有快递地址的说法！自贸区是行政隶属关系用途。可为某个Unit设置隶属的自贸区Adminunit但是unit.company.ad却是普通的地理上的区划,两个Adminunit不一致。
 * 自贸区是监察的概念，本平台不需要涉及自贸区。
 * 针对性添加Secondary索引： @Index(columnList = "code, version有顺序的") 搜索提速；
* */
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(indexes={@Index(columnList = "areacode",unique=true),
        @Index(columnList = "town_id"),
}
)
public class Adminunit implements Node {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;
    //用来合成地址给人看的：
    //前缀行政地理描述部分， 规范地址命名空间
    //在最小的行政单元内部　prefix　名称唯一的。
    //地址名字不一定要指明街道名称的。 五四路241号XX大厦23#308;
    private String  prefix;    //街道'乡'镇';但允许街道名称省略掉。 鼓楼区就行，不一定要加上街道称呼。
    //旧平台 ， 外部地理系统的对接的 地区码。
    //行政区划代码9位数字;350100 福州市; 350101 市辖区　350102 鼓楼区 350181 福清市　350182 长乐区市; 福建省350000;

    //平潭和福州区划代码如何区分开        //编码规则已经被破坏。
    //行政区划代码9位数字编码失去意义了。 第X位 12|34|56|789位数的代码。省-市-县-镇的。
    //行政区划代码必须唯一的。但可允许没有行政区划代码=''的，国外的地址？高新区自贸区的没有分配区划代码。
    //目前福建省内的areacode都是唯一的，虽然有诸多奇怪的Town名字。
    //当前在旧平台获得乡镇级别 不是九位,而是八位代码！ 怪了。
    //如果不是最小行政级别的 地区码 可能较少位数。
    private String  areacode;
    //老去的 邮政编码；
    private String  zipcode;

    //一个最小行政单元Adminunit底下的　所有已经声明的地址。
  //  @OneToMany(mappedBy = "ad")
  //  private Set<Address>  adrs;
    //JoinColumn 的 name 和 referencedColumnName 指的都是数据库的字段名，不是 Entity 的属性。
    //行政区划4个等级+1的； 用于提高搜索判定速度。
    //1:1关联； Adminunit本id对应Town的ID； 本来应当这张表添加1:1关联id字段。
    //1 ：1关系，关系是本类来维护，添加外键指向对方实体表的主键；
    //本类来维护1：1缺省的字段关联名字；@JoinColumn(name = "townID我这一边的关联字段不一定是id", referencedColumnName = "ID是对方的ＩＤ")
    /**
     * ID类型迁移ERROR: unsupported comparison operator: <uuid> = <int>做了关联关系的双方对接ID也要类型一致即使还没数据的,需检查数据库的实际类型。
     * */
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn( referencedColumnName = "ID")
    private Town town;         //最小的1:1关系。

    //树状的多层次的关联关系的处理：短路化，方便搜索过滤。
/**
 * 一个Adminunit是否被一组[Adminunit]s所包含数据库SQL匹配呢？ OR in([admins]exists) 反向搜索,areacode[] strstr函数([],A);
 *   ( )OR(o.county==? && S.town==null) OR (o.city==? && S.county==null)OR()OR()
 *   单个Adminunit配对单个ES搜索如何匹配?，B管辖包含A关系成立：|| (A.county==B.county && B.town=null) || (A.city==B.city && B.county=null) ||
 * */

    //旧平台做法： 数据库表上，树状管理模式，子节点父节点，节点类型，压缩到唯一一个自关联的数据表格。
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "county_id")
    private County  county;

    //允许Adminunit指定定位到不是Town最小级别的情况： 比如国外的地址关联Adminunit可指定到大的城市级别/省份级。
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "city_id")
    private City city;
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "province_id")
    private Province province;
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "country_id")
    private Country country;

    //关联上：一个乡镇社区行政@最小#单元底下的，所有的 俗称的 楼盘。
    //【特别】高级别行政管理单元不要关联楼盘，楼盘尽量对接到实际上最小级别的区划单元。
    @OneToMany(mappedBy = "ad")
    private List<Village> vlgs;

    /**合成的行政区划名字"全面占坑的"， 与prefix“简化邮寄地址”有差异的。
     * */
    @Transient
    public String name(){
        StringBuilder sBuilder=new StringBuilder().append(country.getName());
        if(null!=province) {
            sBuilder.append(province.getName());
            if(null!=city) {
                sBuilder.append(city.getName());
                if(null!=county) {
                    sBuilder.append(county.getName());
                    if(null!=town)
                        sBuilder.append(town.getName());
                }
            }
        }
        return  sBuilder.toString();
    }
    /**也可以考虑？：抛给前端自己过滤啊，前端可以主动屏蔽显示列表，数据后端全部发给前端。
     * 非DB操作：
     * 必须先选有 街道乡镇 后，才能挑选楼盘的。 减少vlgs集合大小！
     * */
    @Transient
    public List<Village>  vlgs(String partial){
        if(!StringUtils.hasText(partial))
            return  vlgs;
        List<Village>  outs= vlgs.stream().filter(village ->
                        (village.getName().contains(partial) )
        ).collect(Collectors.toList());
        //像这样内存过滤速度慢！
        return  outs;
    }


    public String id() {
        return Tool.toGlobalId(this.getClass().getSimpleName(), String.valueOf(this.id));
    }


}


/*
行政区划代码代码从左至右的含义是：第一、二位表示省（自治区、直辖市、特别行政区）、第三、四位表示市（地区、自治州、盟及国家直辖市所属市辖区和县的汇总码）、
第五、六位表示县（市辖区、县级市、旗）、第七至九位表示乡、镇（街道办事处）。
邮政编码6位数编码,前两位数字表示省（直辖市、自治区）；前三位数字表示邮区；前四位数字表示县（市）；最后两位数字表示投递局（所）。
中国有省级34个，地级333个，县级2862个，乡镇级41636个。
*/

//CascadeType.ALL（各种级联操作）CascadeType.DETACH  默认情况没有级联操作。  https://www.jianshu.com/p/e8caafce5445
